/**
 *  @file
 *  @author Stefan Frings
 */

#ifndef TEMPLATE_H
#define TEMPLATE_H

#include <QString>
#include <QRegExp>
#include <QIODevice>
#include <QTextCodec>
#include <QFile>
#include <QString>
#include "templateglobal.h"

namespace stefanfrings {

/**
 *  Enhanced version of QString for template processing. Templates
 *  are usually loaded from files, but may also be loaded from
 *  prepared Strings.
 *  Example template file:
 *  <p><code><pre>
 *  Hello {username}, how are you?
 *
 *  {if locked}
 *    Your account is locked.
 *  {else locked}
 *    Welcome on our system.
 *  {end locked}
 *
 *  The following users are on-line:
 *    Username       Time
 *  {loop user}
 *    {user.name}    {user.time}
 *  {end user}
 *  </pre></code></p>
 *  <p>
 *  Example code to fill this template:
 *  <p><code><pre>
 *  Template t(QFile("test.tpl"),QTextCode::codecForName("UTF-8"));
 *  t.setVariable("username", "Stefan");
 *  t.setCondition("locked",false);
 *  t.loop("user",2);
 *  t.setVariable("user0.name,"Markus");
 *  t.setVariable("user0.time,"8:30");
 *  t.setVariable("user1.name,"Roland");
 *  t.setVariable("user1.time,"8:45");
 *  </pre></code></p>
 *  <p>
 *  The code example above shows how variable within loops are numbered.
 *  Counting starts with 0. Loops can be nested, for example:
 *  <p><code><pre>
 *  &lt;table&gt;
 *  {loop row}
 *    &lt;tr&gt;
 *    {loop row.column}
 *        &lt;td&gt;{row.column.value}&lt;/td&gt;
 *    {end row.column}
 *    &lt;/tr&gt;
 *  {end row}
 *  &lt;/table&gt;
 *  </pre></code></p>
 *  <p>
 *  Example code to fill this nested loop with 3 rows and 4 columns:
 *  <p><code><pre>
 *  t.loop("row",3);
 *
 *  t.loop("row0.column",4);
 *  t.setVariable("row0.column0.value","a");
 *  t.setVariable("row0.column1.value","b");
 *  t.setVariable("row0.column2.value","c");
 *  t.setVariable("row0.column3.value","d");
 *
 *  t.loop("row1.column",4);
 *  t.setVariable("row1.column0.value","e");
 *  t.setVariable("row1.column1.value","f");
 *  t.setVariable("row1.column2.value","g");
 *  t.setVariable("row1.column3.value","h");
 *
 *  t.loop("row2.column",4);
 *  t.setVariable("row2.column0.value","i");
 *  t.setVariable("row2.column1.value","j");
 *  t.setVariable("row2.column2.value","k");
 *  t.setVariable("row2.column3.value","l");
 *  </pre></code></p>
 *  @see TemplateLoader
 *  @see TemplateCache
 */

class DECLSPEC Template :
  public QString
{
public:
  /**
   *  Constructor that reads the template from a string.
   *  @param source The template source text
   *  @param sourceName Name of the source file, used for logging
   */
  Template(const QString source, const QString sourceName);

  /**
   *  Constructor that reads the template from a file. Note that this class does not
   *  cache template files by itself, so using this constructor is only recommended
   *  to be used on local filesystem.
   *  @param file File that provides the source text
   *  @param textCodec Encoding of the source
   *  @see TemplateLoader
   *  @see TemplateCache
   */
  Template(QFile& file, const QTextCodec *textCodec);

  /**
   *  Replace a variable by the given value.
   *  Affects tags with the syntax
   *
   *  - {name}
   *
   *  After settings the
   *  value of a variable, the variable does not exist anymore,
   *  it it cannot be changed multiple times.
   *  @param name name of the variable
   *  @param value new value
   *  @return The count of variables that have been processed
   */
  int setVariable(const QString name, const QString value);

  /**
   *  Set a condition. This affects tags with the syntax
   *
   *  - {if name}...{end name}
   *  - {if name}...{else name}...{end name}
   *  - {ifnot name}...{end name}
   *  - {ifnot name}...{else name}...{end name}
   *
   *  @param name Name of the condition
   *  @param value Value of the condition
   *  @return The count of conditions that have been processed
   */
  int setCondition(const QString name, bool value);

  /**
   *  Set number of repetitions of a loop.
   *  This affects tags with the syntax
   *
   *  - {loop name}...{end name}
   *  - {loop name}...{else name}...{end name}
   *
   *  @param name Name of the loop
   *  @param repetitions The number of repetitions
   *  @return The number of loops that have been processed
   */
  int loop(QString name, const int repetitions);

  /**
   *  Enable warnings for missing tags
   *  @param enable Warnings are enabled, if true
   */
  void enableWarnings(const bool enable = true);

private:
  /** Name of the source file */
  QString sourceName;

  /** Enables warnings, if true */
  bool warnings;
};

} // end of namespace

#endif // TEMPLATE_H
